/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.sys>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: fdbus_service.cpp
 *
 * Purpose: fdbus binding for service
 *
 * Developer:
 *   wen.gu , 2021-05-10
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "fdbus_service.h"

#include "message_builder.h"
#include "log.h"
#include <map>
#include <atomic>
/******************************************************************************
 **    MACROS
 ******************************************************************************/


/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/



///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////



/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
FDBusService::FDBusService(const std::string& service_name)
    :CBaseServer(service_name.c_str()),
    ServiceProvider(service_name)
{
    /** todo something */
}

FDBusService::~FDBusService()
{
    /** todo something */
    StopService();
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool FDBusService::OfferService()
{
    FDB_CONTEXT->start();
    socket_id_ = bind();
    return (socket_id_ != FDB_INVALID_ID);
}

bool FDBusService::StopService()
{
    if (socket_id_ != FDB_INVALID_ID)
    {
        unbind(socket_id_);
        socket_id_ = FDB_INVALID_ID;
        return true;
    }

    return false;
}

bool FDBusService::RegisterReceiveHandler(MessageType type,  MessageId msg_id, MethodHandler handler)
{
    switch (type)
    {
    case MessageType::kRequestOneShot:
    case MessageType::kRequest:  request_map_[msg_id].method = handler; break;
    case MessageType::kFieldSet: field_map_[msg_id].method = handler;   break;
    case MessageType::kFieldGet: field_map_[msg_id].getter = handler;   break;
    default:
        LOGE("unsppoted message type(%d) for register receive handler\n", type);
        return false;
        break;
    }

    return true;
}

DeserializerPtr FDBusService::GetDeserializer(Message::PayloadPtr payload)
{
    return std::make_shared<Deserializer>(payload);    
}

SerializerPtr FDBusService::GetSerializer()
{
    return std::make_shared<Serializer>();
}

bool FDBusService::Send(MessagePtr msg)
{
    if (msg)
    {
        MessageType mt = msg->type();
        switch (mt)
        {
        case MessageType::kEvent:       return SendEvent(msg) break;
        case MessageType::kResponse:    return SendResponse(msg) break;
        case MessageType::kFieldNotify: return SendEvent(msg); break;
        case MessageType::kFieldSetAck: return SendResponse(msg); break; 
        case MessageType::kFieldGetAck: return SendResponse(msg); break;        
        default:
            LOGE("unsupported message type(%d) for service to send\n", mt);
            break;
        }
    }

    return false;
}

MessagePtr FDBusService::MakeMessage(MessageType type)
{
    MessagePtr msg = std::make_shared<Message>(type); /** todo refine me? */

    if (msg)
    {
        msg->set_session_id(getNextSessionId());
    }

    return msg;
}

void FDBusService::onInvoke(CBaseJob::Ptr &msg_ref)
{
    auto msg = castToMessage<CBaseMessage *>(msg_ref);

    if (msg == nullptr)
    {
        return ;
    }
    MessageId msg_id = (MessageId)msg->code();

    MyMessagePtr my_msg = MAKE_MY_MESSAGE(); 
    MessagePtr msg_ptr = std::dynamic_pointer_cast<Message>(my_msg);

    MessageParser parser(msg_ptr);
    if (!msg->deserialize(parser))
    {
        msg->status(msg_ref, NFdbBase::FDB_ST_MSG_DECODE_FAIL, "Fail to decode request!");
        LOGE("deserialize received message(id:%d) failed\n", msg_id);
        return;
    }

    if (MSG_IS_METHOD(msg_id))
    {
        ReceiveHandlerMap::iterator it = method_map_.find(msg_id);

        if (it != method_map_.end())
        {
            my_msg->set_msg_ref(msg_ref);
            ReceiveHandler& rec_hdl = it->second;
            rec_hdl.method(msg_ptr);
        }
        else
        {
            /** todo, process error */
            LOGE("not found receive handler for message id: %d\n", msg_id);
        }             
    }
    else if (MSG_IS_FIELD(msg_id))
    {
        ReceiveHandlerMap::iterator it = field_map_.find(msg_id);

        if (it != field_map_.end())
        {
            my_msg->set_msg_ref(msg_ref);
            ReceiveHandler& rec_hdl = it->second;
            MessageType msg_type = msg_ptr->type();
            if (msg_type == MessageType::kFieldSet)
            {
                if (rec_hdl.method)
                {
                    rec_hdl.method(msg_ptr);
                }
                else
                {
                    LOGE("field(%d), haven't setter method\n", msg_id);
                }
                
            }
            else if (msg_type == MessageType::kFieldGet)
            {
                if (rec_hdl.getter)
                {
                    rec_hdl.getter(msg_ptr);
                }
                else
                {
                    LOGE("field(%d), haven't getter method\n", msg_id);
                }                
            }
            else
            {
                LOGE("unsupported field method: %d\n", msg_type);
            }
        }
        else
        {
            /** todo, process error */
            LOGE("not found receive handler for message id: %d\n", msg_id);
        }   
    }
    else
    {
        LOGE("unsupported message id(0x%08x) for receive handler map\n", msg_id); 
    }
}

/* called when client call subscribe() to register message */
void FDBusService::onSubscribe(CBaseJob::Ptr &msg_ref) 
{
        auto msg = castToMessage<CFdbMessage *>(msg_ref);
        const CFdbMsgSubscribeItem *sub_item;
        /* iterate all message id subscribed */
        FDB_BEGIN_FOREACH_SIGNAL(msg, sub_item)
        {
            FdbMsgCode_t msg_code = sub_item->msg_code();
            const char *filter = "";
            if (sub_item->has_filter())
            {
                filter = sub_item->filter().c_str();
            }
            FdbSessionId_t sid = msg->session();

            if (fdbIsGroup(msg_code))
            {
                LOGI("group message %d, filter %s of session %d is registered!\n", msg_code, filter, sid);
                /** todo, is need publish all events in one group */
                return;
            }
            
            LOGI("single message %d, filter %s of session %d is registered!\n", msg_code, filter, sid);

            /* reply initial value to the client subscribing the message id */
        }
        FDB_END_FOREACH_SIGNAL()
}


bool FDBusService::SendEvent(MessagePtr msg)
{
    MessageBuilder msgb(msg);
    return broadcast(msg->message_id(), msgb, "");
}

bool FDBusService::SendResponse(MessagePtr msg)
{
    MyMessagePtr mmsg = std::dynamic_pointer_cast<MyMessage>(msg);

    if (mmsg)
    {
        auto msg_ptr = castToMessage<CBaseMessage*>(mmsg->msg_ref());

        if (msg_ptr)
        {
            MessageBuilder msgb(msg);
            return msg_ptr->reply(mmsg->msg_ref(), msgb);
        }
    }

    return false;
}




